﻿using System;
using System.Data.OleDb;
using System.Drawing;
using System.Security.Cryptography;
using System.Text;
using System.Timers;
using System.Windows.Forms;

/// Created by Jasper Vlemmings.
/// Edited by:

// TODO: limit login tries before locking application for a period
// TODO: set for settings from db or secured config file( maintain backup possibility in local code)


namespace Team4sCMDB
{
    public partial class Login : Form
    {
        private bool isAdmin;

        public Login()
        {
            InitializeComponent();
            // The Title of the Login Form
            this.Text = "Team4S Login";
        }

        #region Settings for Texts and Values  
        // No Methods in this Region

        /*// Hardcoded Login credentials (better to use a database with encrypted values)
        private static string 
            user = "user", 
            password = "pass";*/

        // Initial displayed values of textboxes, labels and statuslabels
        private const string
            txtBUsername_standardText = "Gebruikersnaam",
            txtBPassword_standardText = "Wachtwoord",
            chBShowPasswordChars_standardText = "Toon wachtwoordkarakters",
            toolStripStatusLabel_standardText = "Welkom";

        // Bool for statuslabel update. This says if this is the first login attempt. This does NOT have any effect on security
        private bool 
            FirstTime = true;

        // Faulty login counter, limits & LockedTimerInterval
        private int
            Faulty, // Retrieved on load
            FaultLimit = 10,
            FaultWarning = 1,
            LockedTimerInterval = 10; // (Seconds) LockedTimer interval

        // The ammount of time before the application is available again after locking down from reaching the Faultlimit
        TimeSpan Lockperiod = new TimeSpan(
        /*Days*/    0,      
        /*hours*/   0,      
        /*minutes*/ 5,     
        /*seconds*/ 0
        /*Close TimeSpan*/);

        // The last moment the application has been locked. 
        DateTime
            Locktime;  // Retrieved on load

        // Update messages for RemainingAttempts()
        private const string
            One_One = "Nog ",                   // "Still"
            One_Two = " pogingen resterend.",   // " n attempts left."
            Two = "Nog één poging resterend.";  // "One attempt left."

        // Error message text for toolStripStatusLabel   
        private string 
            incorrectEntry = "Gebruikersnaam en/of wachtwoord onjuist",
            loginLimitReached_toolStripStatusLabel = "Applicatie geblokkeerd.";
        
        // Error message text for Messagebox
        private const string
            nearLoginLimit = "Waarschuwing: de volgende verkeerde invoer blokkeert de applicatie!",
            loginLimitReached = "Applicatie geblokkeerd. Neem contact op met uw softwareleverancier of systeembeheerder";

        #endregion

        private string RemainingAttempts()
            {
            if((FaultLimit - Faulty) != 1)
                {
                 return One_One + (FaultLimit - Faulty).ToString() + One_Two;
                }
            else
                {
                return Two;
                }
            }

        // Timers
        private static System.Timers.Timer LockedTimer;

        private void Login_Load(object sender, EventArgs e)
        {
            // Set initial textbox style
            loadStyleUsername();
            loadStylePassword();

           // GetFaulty();
            GetLocktime();
            TimeSpan test = new TimeSpan(
            /*Days*/    0,      
            /*hours*/   0,      
            /*minutes*/ 2,     
            /*seconds*/ 0
            /*Close TimeSpan*/);
            //Locktime = DateTime.Now - test;
            

        // Determine if the application is under lockdown
        if (Locktime != DateTime.MinValue && DateTime.Now < (Locktime + Lockperiod))
            {
            Lock();
            }
        else
            {
            UnLock();
            ResetValues();
            toolStripStatusLabel.Text = toolStripStatusLabel_standardText;
            txtBUsername.Text = txtBUsername_standardText;
            txtBPassword.Text = txtBPassword_standardText;
            chBShowPasswordChars.Text = chBShowPasswordChars_standardText;
            }
        }
       
        private void btnLogin_Click(object sender, EventArgs e)
        {
            string pwdHash = CalculateMD5Hash(txtBPassword.Text);

            // check username en password (ToLower() Makes username NOT casesensitive
        if ((checkLogin(txtBUsername.Text.ToLower(),pwdHash))==true)
            {
            // Reset values for next time
            ResetValues();
             

            // Start Main and hide login form
            Main main = new Main(isAdmin);
            this.Hide();
            main.ShowDialog();
            this.Close();
            }
        else
            {
            // Add one attempt to counter and run IncorrectEntry()
            Faulty++;
            IncorrectEntry();
            }
        // Update bool after the first login attempts. Perhaps there is some "allow change once per startup" thing without using the registry?
        FirstTime = false;
        }

        #region Username Text and Font changes

        private void loadStyleUsername()
        {
            txtBUsername.Text = txtBUsername_standardText;
            txtBUsername.ForeColor = SystemColors.ControlDark;
            txtBUsername.Font = new Font(this.Font, FontStyle.Italic);
        }

        private void txtBUsername_Enter(object sender, EventArgs e)
        {
            if ((txtBUsername.Text != txtBUsername_standardText) && (txtBUsername.Text != ""))
            {
                txtBUsername.Text = txtBUsername.Text;
                txtBUsername.ForeColor = SystemColors.ControlText;
                txtBUsername.Font = new Font(this.Font, FontStyle.Regular);
            }
            else
            {
                txtBUsername.Text = "";
                txtBUsername.ForeColor = SystemColors.ControlText;
                txtBUsername.Font = new Font(this.Font, FontStyle.Regular);
            }
        }

        private void txtBUsername_Leave(object sender, EventArgs e)
        {
            if (txtBUsername.Text != String.Empty)
            {
                if (txtBUsername.Text != txtBUsername_standardText)
                {
                }
            }
            else
            {
                txtBUsername.Text = txtBUsername_standardText;
                txtBUsername.ForeColor = SystemColors.ControlDark;
                txtBUsername.Font = new Font(this.Font, FontStyle.Italic);
            }
        }

        #endregion

        #region Wachtwoord Text and Font changes

        private void loadStylePassword()
        {
            txtBPassword.Text = txtBPassword_standardText;
            txtBPassword.ForeColor = SystemColors.ControlDark;
            txtBPassword.Font = new Font(this.Font, FontStyle.Italic);
            txtBPassword.UseSystemPasswordChar = false;
        }

        private void txtBPassword_Enter(object sender, EventArgs e)
            {
            if (chBShowPasswordChars.Checked == false)
                {
                txtBPassword.UseSystemPasswordChar = true;
                }
            else
                {
                txtBPassword.UseSystemPasswordChar = false;
                }
            txtBPassword.Text = "";
            txtBPassword.ForeColor = SystemColors.ControlText;
            txtBPassword.Font = new Font(this.Font, FontStyle.Regular);

            if (FirstTime == false)
                {
                toolStripStatusLabel.BackColor = SystemColors.Control;
                toolStripStatusLabel.ForeColor = SystemColors.ControlText;
                toolStripStatusLabel.Text = RemainingAttempts();
                }

            }

        private void txtBPassword_Leave(object sender, EventArgs e)
        {
            if (txtBPassword.Text != String.Empty)
            {
                if (txtBPassword.Text != txtBPassword_standardText)
                {
                }
            }
            else
            {
                txtBPassword.Text = txtBPassword_standardText;
                txtBPassword.ForeColor = SystemColors.ControlDark;
                txtBPassword.Font = new Font(this.Font, FontStyle.Italic);
                txtBPassword.UseSystemPasswordChar = false;
            }
        }

        // Toggle character visibility of password box
        private void chBShowPasswordChars_CheckedChanged(object sender, EventArgs e)
            {
            if (chBShowPasswordChars.Checked == false && txtBPassword.Text != txtBPassword_standardText)
                {
                txtBPassword.UseSystemPasswordChar = true;
                }
            else
                {
                txtBPassword.UseSystemPasswordChar = false;
                }
            }

        #endregion

        #region Login Error Handling

        private void IncorrectEntry()
            {
            SetFaulty();

            // Change password font after failed login
            txtBPassword.Text = txtBPassword_standardText;
            txtBPassword.ForeColor = SystemColors.ControlDark;
            txtBPassword.Font = new Font(this.Font, FontStyle.Italic);
            txtBPassword.UseSystemPasswordChar = false;

            // Determine Error message and actions
            if (Faulty < FaultLimit) // Why not != ? For the possible event of somone finding a way to raise Faulty above the limit, thus never getting blocked.
                {
                if (Faulty == (FaultLimit - FaultWarning))
                    {
                    NearLoginLimit();
                    toolStripStatusLabel.Text = RemainingAttempts();
                    }
                else
                    {

                    // Username and/or Password incorrect. Show error message in statuslabel
                    toolStripStatusLabel.Text = incorrectEntry;
                    toolStripStatusLabel.BackColor = Color.Red;
                    toolStripStatusLabel.ForeColor = Color.White;
                    }
                }
            else
                {
                LoginLimitReached();
                }
            }

        private void NearLoginLimit()
            {
            // Selectable option. Enable or disable showing a Messagebox in this case
            int Option = 1;

            switch (Option)
	            {
		            default:
                    case 1:
                        MessageBox.Show(nearLoginLimit);
                        break;

                    case 2:
                        // Something else
                        break;
	            }
            }

        private void LoginLimitReached()
            {
            Lock();
            }

        #endregion

        private void ResetValues()
            {
            // This resets the values that need to be reset for the next time you run the application
            FirstTime = true;
            Faulty = 0;
            SetFaulty();
            Locktime = DateTime.MinValue;
            SetLocktime();
            }

        #region Locking and UnLocking

        private void Lock()
            {
                statusStripLogin.Visible = false;
            /*
            // Show Messagebox with notification and show ToolStripStatusLabel
            MessageBox.Show(loginLimitReached);
            toolStripStatusLabel.Text = loginLimitReached_toolStripStatusLabel;
            toolStripStatusLabel.BackColor = Color.Red;
            toolStripStatusLabel.ForeColor = Color.White;

            // Disable All Controls
            foreach (Control c in this.Controls)
                {
                c.Enabled = false;
                }
            // Reënable statusStrip for better readability of message
            statusStripLogin.Enabled = true;

            // Set Locktime and write Locktime to db if ther is no Locktime in the time of the Lockperiod
            if (Locktime != DateTime.MinValue && DateTime.Now > (Locktime + Lockperiod))
                {
                SetLocktime();
                }
           
            //// Start LockedTimer and act every n seconds
            //LockedTimer = new System.Timers.Timer((LockedTimerInterval * 1000)); // (LockedTimerInterval * 1000) Seconds to milliseconds
            //LockedTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            //LockedTimer.Enabled = true;

            // Perhaps send an automated e-mail to administrator? Include Locktime.

            // Enable Hidden Administrator Functions
            */
            }

        private void UnLock()
            {
            // Enable all controls 
            foreach (Control c in this.Controls)
                {
                c.Enabled = true;
                }
            // Reset Faulty login counter and reset Locktime
            Faulty = 0;
            SetFaulty();
            Locktime = DateTime.MinValue;
            SetLocktime();

            // Change Statuslabel looks 
            toolStripStatusLabel.BackColor = SystemColors.Control;
            toolStripStatusLabel.ForeColor = SystemColors.ControlText;
            }

        private void OnTimedEvent(object source, ElapsedEventArgs e)
            {
            if (DateTime.Now > (Locktime+Lockperiod))
                {
                LockedTimer.Enabled = false;
                UnLock();
                }
            }

        #endregion

        private string CalculateMD5Hash(string input)
        {
            // step 1, calculate MD5 hash from input
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.ASCII.GetBytes(input);
            byte[] hash = md5.ComputeHash(inputBytes);

            // step 2, convert byte array to hex string
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString();
        }

        #region Database Methods

        private bool checkLogin(string username, string pwdHash) 
        {
            
            const int admingroep = 3; // groep 3 mag wijzigen en toevoegen
            string hash = executeSelectStatement("SELECT md5hash FROM gebruiker WHERE gebruikersnaam = '" + username + "';");
            int groep = Convert.ToInt32(executeSelectStatement("SELECT gebruikersgroepID FROM gebruiker WHERE gebruikersnaam = '" + username + "';"));

            // is user Admin?
            if ((hash == pwdHash) && (groep == admingroep))
            {
                isAdmin = true;
                return true;
            }
            else 
            {
                //is user known?
                if ((hash == pwdHash))
                {
                    isAdmin = false;
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        private string executeSelectStatement(string query)
        {
            string result = "";
            OleDbConnection connection = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Team4sCmdb.accdb;Persist Security Info=False");
            OleDbCommand command; 
            command = new OleDbCommand(query, connection);
            connection.Open();
            OleDbDataReader reader;
            try
            {
                reader = command.ExecuteReader();
                reader.Read();
                result=reader[0].ToString();
                return result;
            }
            catch (Exception)
            {
                // no catch
            }

            connection.Close();
            return null;   
        }

        # region Unused methods
        private void GetFaulty()
            {
            //Having TABLE Logindata, with Column; faulty
            string sql = "SELECT faulty FROM Logindata WHERE user = '" + "" + "';";
            // Check if there is any value entered. If not, then 0 is set. Else faulty is loaded from db.
            if (sql == null || sql == "")
                {
                Faulty = 0;
                }
            else
                {
                Faulty = Convert.ToInt32(sql);
                }
            }

        private void SetFaulty()
            {
            //Having TABLE Logindata, with Column; faulty
            string sql = "UPDATE Logindata SET faulty = '" + Faulty.ToString() + "' WHERE user = '" + "" + "';";
            }

        private void GetLocktime()
            {
            //Having TABLE Logindata, with Column; locktime
            string sql = "SELECT locktime FROM Logindata WHERE user = '" + "" + "';";
            
            // Check if there is any value entered. If not, then MinValue is set. Else locktime is loaded from db.
            if (sql == null || sql == "")
                {
                Locktime = DateTime.MinValue;
                }
            else
                {
                Locktime = Convert.ToDateTime(sql);
                }
            }

        private void SetLocktime()
            {
            //Having TABLE Logindata, with Column; locktime
            string sql = "UPDATE Logindata SET locktime = '" + Locktime.ToString() + "' WHERE user = '" + "" + "';";
            }
        # endregion unused methods

        #endregion


        #region TEST BUTTONS (Remove before publishing!)

        private void btnBypassLogin_Click(object sender, EventArgs e)
            {
            // Start Main 
            Main main = new Main(true);
            this.Hide();
            main.ShowDialog();
            this.Close();
            }

        private void btnShowErrors_MouseUp(object sender, MouseEventArgs e)
            {
            // Allows for testing of three error actions to be tested by clicking the button with a mouse
            switch (e.Button.ToString())
                {
                default:
                case "Left":
                    IncorrectEntry();
                    break;

                case "Middle":
                    //NearLoginLimit();
                    string h = CalculateMD5Hash(txtBPassword.Text);
                    Clipboard.SetText(h);
                    MessageBox.Show(h.ToString());
                    break;

                case "Right":
                    LoginLimitReached();
                    break;
                }
            }
        
        #endregion
        
     }
}
